{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "MNIST的代码例程  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我自己的beadline代码  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 一、库导入"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %%writefile AI/code/lib/imports.py\n",
    "# %load AI/code/lib/imports.py\n",
    "\n",
    "# 系统库\n",
    "import os\n",
    "import time\n",
    "import pathlib\n",
    "import argparse\n",
    "\n",
    "# 数据图像处理\n",
    "import numpy as np \n",
    "import pandas as pd \n",
    "import matplotlib.pyplot as plt \n",
    "\n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "# PyTorch框架\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from torchvision import transforms, models\n",
    "import torchvision.datasets as datasets\n",
    "from torch.utils.data import SubsetRandomSampler\n",
    "from torch.autograd import Variable\n",
    "from torch import nn, optim\n",
    "import torch.nn.functional as F\n",
    "\n",
    "import kaggle  # AI比赛数据下载"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "^C\n",
      "\n",
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%conda install sklearn"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 二、环境配置"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## jupyter使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting AI/code/help/jupyter.md\n"
     ]
    }
   ],
   "source": [
    "%%writefile AI/code/help/jupyter.md\n",
    "# %load AI/code/help/jupyter.md\n",
    "# jupyter notebook使用\n",
    "\n",
    "# 查看变量\n",
    "?traindata\n",
    "\n",
    "# 查看库文件内容\n",
    "??os\n",
    "\n",
    "# 查看类\n",
    "???dta\n",
    "\n",
    "# 删除一个变量\n",
    "%xdel model\n",
    "\n",
    "# 自动导入\n",
    "%reload_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 自动导入"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "%reload_ext autoreload\n",
    "%autoreload 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 参数配置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %%writefile AI/code/config/parameter.py\n",
    "parser = argparse.ArgumentParser(description='PyTorch ImageNet Training')\n",
    "parser.add_argument('--data', metavar='DIR',\n",
    "                    help='数据的路径')\n",
    "parser.add_argument('--j', '--workers', default=4, type=int, metavar='N',\n",
    "                    help='数据导入的线程 (默认为: 4)')\n",
    "parser.add_argument('--epochs', default=90, type=int, metavar='N',\n",
    "                    help='训练的次数')\n",
    "parser.add_argument('--start-epoch', default=0, type=int, metavar='N',\n",
    "                    help='开始的次数 (useful on restarts)')\n",
    "parser.add_argument('--b', '--batch-size', default=16, type=int,\n",
    "                    metavar='N',\n",
    "                    help='批处理大小（默认为16）')\n",
    "parser.add_argument('--lr', '--learning-rate', default=[0.001, 0.1, 0.33, 0.000005], type=float,\n",
    "                    metavar='LR', help='初始化的学习率', dest='lr')\n",
    "parser.add_argument('--setlr', '--learning-change', default=[1, 3, 6], type=int,\n",
    "                     help='初始化的学习率', dest='setlr')\n",
    "parser.add_argument('--momentum', default=0.9, type=float, metavar='M',\n",
    "                    help='动量')\n",
    "parser.add_argument('--wd', '--weight-decay', default=1e-4, type=float,\n",
    "                    metavar='W', help='权重下降 (默认: 1e-4)',\n",
    "                    dest='weight_decay')\n",
    "parser.add_argument('--p', '--print-freq', default=10, type=int,\n",
    "                    metavar='N', help='打印频率 (默认: 10)')\n",
    "parser.add_argument('--resume', default=None, type=str, metavar='PATH',\n",
    "                    help='恢复的模型 (默认: 空)')\n",
    "parser.add_argument('--e', '--evaluate', dest='evaluate', action='store_true',\n",
    "                    help='evaluate model on validation set')\n",
    "parser.add_argument('--pretrained', dest='pretrained', action='store_true',\n",
    "                    help='使用训练模型')\n",
    "parser.add_argument('--world-size', default=-1, type=int,\n",
    "                    help='分布式训练的节点数')\n",
    "parser.add_argument('--rank', default=-1, type=int,\n",
    "                    help='分布式训练的节点等级')\n",
    "parser.add_argument('--dist-url', default='tcp://224.66.41.62:23456', type=str,\n",
    "                    help='用于设置分布式培训的url')\n",
    "parser.add_argument('--dist-backend', default='nccl', type=str,\n",
    "                    help='分布式后端')\n",
    "parser.add_argument('--seed', default=None, type=int,\n",
    "                    help='初始化培训的种子. ')\n",
    "parser.add_argument('--gpu', default=0, type=int,\n",
    "                    help='GPU使用的ID.')\n",
    "parser.add_argument('--multiprocessing-distributed', action='store_true',\n",
    "                    help='使用多处理分布式培训来启动每个节点有N个进程，'\n",
    "                         '其中有N个GPU。这是对单个节点或PyTorch使用'\n",
    "                         'PyTorch的最快方法多节点数据并行训练')\n",
    "\n",
    "parser.add_argument('--num_classes', type=int, default=10, help='您的任务应该分类的类数')\n",
    "parser.add_argument('--local_data_root', default='/cache/', type=str,\n",
    "                    help='a directory used for transfer data between local path and OBS path')\n",
    "parser.add_argument('--data_url', type=str, help='the training and validation data path')\n",
    "parser.add_argument('--test_data_url', default='', type=str, help='the test data path')\n",
    "parser.add_argument('--data_local', default='', type=str, help='the training and validation data path on local')\n",
    "parser.add_argument('--test_data_local', default='', type=str, help='the test data path on local')\n",
    "parser.add_argument('--train_url', type=str, help='the path to save training outputs')\n",
    "parser.add_argument('--train_local', default='', type=str, help='the training output results on local')\n",
    "parser.add_argument('--tmp', default='', type=str, help='a temporary path on local')\n",
    "parser.add_argument('--deploy_script_path', default='', type=str,\n",
    "                    help='a path which contain config.json and customize_service.py, '\n",
    "                         'if it is set, these two scripts will be copied to {train_url}/model directory')\n",
    "\n",
    "args = parser.parse_args(args=[]) # 实际使用要删除"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Namespace(b=16, data=None, data_local='', data_url=None, deploy_script_path='', dist_backend='nccl', dist_url='tcp://224.66.41.62:23456', epochs=90, evaluate=False, gpu=0, j=4, local_data_root='/cache/', lr=[0.001, 0.1, 0.33, 5e-06], momentum=0.9, multiprocessing_distributed=False, num_classes=10, p=10, pretrained=False, rank=-1, resume=None, seed=None, setlr=[1, 3, 6], start_epoch=0, test_data_local='', test_data_url='', tmp='', train_local='', train_url=None, weight_decay=0.0001, world_size=-1)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "args"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 数据处理"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %%writefile AI/code/dataprocess/digital-recognizer.py\n",
    "# %load AI/code/dataprocess/digital-recognizer.py\n",
    "# 数据处理，返回的是numpy类型的数据\n",
    "import kaggle\n",
    "import os\n",
    "\n",
    "\n",
    "traindata = pathlib.Path(\"data\\\\digit-recognizer\\\\train.csv\")\n",
    "testdata = pathlib.Path(\"data\\\\digit-recognizer\\\\test.csv\")\n",
    "sample_subdata = pathlib.Path(\"data\\\\digit-recognizer\\\\knn_benchmark.csv\")\n",
    "\n",
    "digital_train_data = pd.read_csv(traindata, dtype=np.float32)\n",
    "digital_test_data = pd.read_csv(testdata, dtype=np.float32)\n",
    "# train.label.head()\n",
    "\n",
    "# 分割数据\n",
    "targets_np = digital_train_data.label.values\n",
    "features_np = digital_train_data.loc[:, digital_train_data.columns != 'label'].values/255\n",
    "\n",
    "final_test_np = digital_test_data.values/255\n",
    "test_tn = torch.from_numpy(final_test_np)\n",
    "\n",
    "# 创建一个虚拟的标志\n",
    "test_target = np.zeros(final_test_np.shape)\n",
    "test_target = torch.from_numpy(test_target)\n",
    "\n",
    "# 分割训练与测试集\n",
    "features_train, features_val, target_train, target_val = train_test_split(features_np, targets_np, test_size=0.2, random_state=42)\n",
    "\n",
    "\n",
    "train_data = features_train\n",
    "train_target = torch.from_numpy(target_train).type(torch.LongTensor) # data type is long\n",
    "\n",
    "# create feature and targets tensor for test set.\n",
    "val_data = features_val\n",
    "val_target = torch.from_numpy(target_val).type(torch.LongTensor) # data type is long \n",
    "\n",
    "test_data = final_test_np\n",
    "test_target = test_target.type(torch.LongTensor) # data type is long \n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 数据加载器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %load AI/code/dataloader/data_dataloader.py\n",
    "# %load AI/code/dataloader/data_dataloader.py\n",
    "# 制作数据的数据导入器\n",
    "'''\n",
    "数据输入：numpy\n",
    "类型：\n",
    "默认值：\n",
    "\n",
    "数据输出：Tensor\n",
    "类型：\n",
    "默认值：\n",
    "'''\n",
    "\n",
    "train_data_transform = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize([0.5], [0.5])\n",
    "])\n",
    "val_transform = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize([0.5], [0.5])\n",
    "])\n",
    "test_transform = transforms.Compose([\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize([0.5], [0.5])\n",
    "])\n",
    "\n",
    "# 设置batch大小\n",
    "train_batch_size = 256\n",
    "val_batch_size = 256\n",
    "test_batch_size = 256\n",
    "\n",
    "# DataSet类 - （数字类）\n",
    "train_dataset = torch.utils.data.TensorDataset(torch.from_numpy(train_data), train_target)\n",
    "val_dataset = torch.utils.data.TensorDataset(torch.from_numpy(val_data), val_target)\n",
    "test_dataset = torch.utils.data.TensorDataset(torch.from_numpy(test_data), test_target)\n",
    "\n",
    "# 数据加载器\n",
    "train_loader = torch.utils.data.DataLoader(train_dataset, batch_size = train_batch_size, shuffle = True)\n",
    "val_loader = torch.utils.data.DataLoader(val_dataset, batch_size = val_batch_size, shuffle = True)\n",
    "test_loader = torch.utils.data.DataLoader(test_dataset, batch_size = test_batch_size, shuffle = False)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型建立"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model isn't have pretrained model\n"
     ]
    }
   ],
   "source": [
    "# %%writefile AI/code/models/classifier.py\n",
    "import os,sys\n",
    "from torchvision import models\n",
    "from AI.models.efficientnet import EfficientNet\n",
    "\n",
    "# 以脚本执行时允许相对导入\n",
    "if __name__ == \"__main__\" and __package__ is None:\n",
    "    sys.path.insert(0, os.path.join(os.getcwd(), '..', '..', '..'))\n",
    "    import AI  # noqa: F401\n",
    "    __package__ = \"AI\"\n",
    "\n",
    "from AI.models.classifier import  Classifier\n",
    "\n",
    "if args.pretrained:\n",
    "    print(\"Model have pretrained model\")\n",
    "#     model = models.resnet50(pretrained=True)\n",
    "#     model.fc = nn.Sequential(nn.Linear(2048,512),nn.Linear(512,10),)\n",
    "#     model = models.resnet18(pretrained=True)\n",
    "#     model.fc = nn.Sequential(\n",
    "#         nn.Linear(512,10),)\n",
    "#     model = EfficientNet.from_pretrained('efficientnet-b0') # 加载预训练模型\n",
    "else:\n",
    "    print(\"Model isn't have pretrained model\")\n",
    "#     model = EfficientNet.from_name('efficientnet-b0') # 不加载预训练模型\n",
    "    model = Classifier()\n",
    "#     model = models.resnet50(pretrained=False)\n",
    "#     model.fc = nn.Sequential(nn.Linear(2048,512),nn.Linear(512,10),)\n",
    "#     model = models.resnet18(pretrained=False)\n",
    "#     model.fc = nn.Sequential(\n",
    "#         nn.Linear(512,10),)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "%xdel Classifier\n",
    "%xdel model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 10])"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model(torch.rand(1,3,224,224)).shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %%writefile AI/code/models/criterions.py \n",
    "# %load AI/code/models/criterions.py\n",
    "# 损失函数\n",
    "# 多分类（cross entropy）\n",
    "# criterion = nn.CrossEntropyLoss()\n",
    "# 多标签分类（binary cross entropy）\n",
    "# 语义分割（binary cross entropy\n",
    "# 如果是多类语义分割，可选cross entropy）\n",
    "\n",
    "criterion = nn.NLLLoss()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "# %%writefile AI/code/models/optimizers.py\n",
    "# %load AI/code/models/optimizers.py\n",
    "# 优化器\n",
    "\n",
    "# 常用Adam优化器， 细致调参时再用SGD\n",
    "# Adam: init_lr=5e-4(3e-4)    \n",
    "# 衰减步骤 [5e-4(3e-4), 1e-4, 1e-5, 1e-6]\n",
    "# optimizer = optim.SGD(model.parameters(), lr = 0.01, momentum=0.9)\n",
    "# optimizer = optim.Adam([var1, var2], lr = 0.0001)\n",
    "# optim.SGD([\n",
    "#                 {'params': model.base.parameters()},\n",
    "#                 {'params': model.classifier.parameters(), 'lr': 1e-3}\n",
    "#             ], lr=1e-2, momentum=0.9)\n",
    "# optimizer = torch.optim.Adam([{'params': model.backbone.parameters(), 'lr': 3e-5},\n",
    "#                               {'params': model.fc.parameters(), 'lr': 3e-4}, ])\n",
    "optimizer = optim.Adam(model.parameters(), lr=3e-4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型训练与验证"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting AI/code/trainval/class_train.py\n"
     ]
    }
   ],
   "source": [
    "# %%writefile AI/code/trainval/class_train.py\n",
    "# %load AI/code/trainval/class_train.py\n",
    "from AI.utils.train_utils import get_accuracy, save_checkpoint, logger, adjust_learning_rate\n",
    "\n",
    "def main():\n",
    "    train_losses, val_losses, val_accuracyes = main_worker(15, gpu_flag=True)\n",
    "    return train_losses, val_losses, val_accuracyes\n",
    "\n",
    "def main_worker(num_epoch, gpu_flag=False):\n",
    "    train_log = logger('runs//train')\n",
    "    val_log = logger('runs//val')\n",
    "    \n",
    "    if args.resume:\n",
    "        print(\"=> loading checkpoint '{}'\".format(args.resume))\n",
    "        if args.gpu is None:\n",
    "            checkpoint = torch.load(args.resume)\n",
    "        else:\n",
    "            # Map model to be loaded to specified single gpu.\n",
    "            loc = 'cuda:{}'.format(args.gpu)\n",
    "            checkpoint = torch.load(args.resume, map_location=loc)\n",
    "        args.start_epoch = checkpoint['epoch']\n",
    "        best_acc1 = checkpoint['best_acc1']    \n",
    "        model.load_state_dict(checkpoint['state_dict'])\n",
    "        optimizer.load_state_dict(checkpoint['optimizer'])\n",
    "        print(\"=> loaded checkpoint '{}' (epoch {})\"\n",
    "              .format(args.resume, checkpoint['epoch']))\n",
    "        \n",
    "    train_losses, val_losses = [], []\n",
    "    val_accuracyes = []\n",
    "    best_acc1 = 0\n",
    "    tmp_time=start_time = time.time()\n",
    "    # 开始训练\n",
    "    print('Start training!')\n",
    "    for epoch in range(args.start_epoch, args.epochs):\n",
    "        adjust_learning_rate(optimizer, epoch, args.lr, args.setlr)\n",
    "        \n",
    "        train_out = train_model(epoch, train_loader, model, criterion, optimizer, gpu_flag)\n",
    "        val_out = val_model(epoch, val_loader, model, criterion, optimizer, gpu_flag)\n",
    "        train_log.add_train_data(train_out, epoch)\n",
    "        val_log.add_train_data(val_out, epoch)\n",
    "        is_best = val_out[1] > best_acc1\n",
    "        best_acc1 = max(val_out[1], best_acc1)\n",
    "        save_checkpoint({\n",
    "                'epoch': epoch + 1,\n",
    "                'arch': model,\n",
    "                'state_dict': model.state_dict(),\n",
    "                'best_acc1' : best_acc1,\n",
    "                'acc1': val_out[1],\n",
    "                'acc5': val_out[2],\n",
    "                'optimizer' : optimizer.state_dict(),\n",
    "            },is_best, 'resnet50')\n",
    "\n",
    "        train_losses.append(train_out[0])\n",
    "        val_losses.append(val_out[0])\n",
    "        val_accuracyes.append(val_out[1])\n",
    "        print(\"Epoch: {epoch} Loss_avg：{loss:6.4f} \\t Acc@1_avg {acc1:6.2f} \\t Acc@5_avg {acc5:6.2f} \\t \\\n",
    "Time@ {time:6.2f}\\t TotalTime@{ttime:6.2f}\"\n",
    "                                                          .format(epoch=epoch,\n",
    "                                                                  loss=train_out[0],\n",
    "                                                                  acc1=val_out[1],\n",
    "                                                                  acc5=val_out[2],\n",
    "                                                                  time=time.time()-tmp_time,\n",
    "                                                                  ttime=time.time()-start_time))\n",
    "        tmp_time = time.time()\n",
    "    print('Finished training!')\n",
    "    train_log.close()\n",
    "    val_log.close()\n",
    "    \n",
    "    return train_losses, val_losses, val_accuracyes\n",
    "    \n",
    "def train_model(epoch, data_loader, model, cirterion, optimizer, gpu_flag=False):\n",
    "    '''\n",
    "    模型的训练函数\n",
    "    '''\n",
    "    if gpu_flag:\n",
    "        model.cuda().train()\n",
    "    else:\n",
    "        model.cpu().train()\n",
    "        \n",
    "    loss_avg = 0.0\n",
    "    acc1_avg=0.0\n",
    "    acc5_avg=0.0\n",
    "    for i, data in enumerate(data_loader):\n",
    "        images, labels = data\n",
    "        if gpu_flag:\n",
    "            images = Variable(images).cuda()\n",
    "            labels = Variable(labels).cuda()\n",
    "        else:\n",
    "            images = Variable(images)\n",
    "            labels = Variable(labels)\n",
    "        optimizer.zero_grad()\n",
    "        log_ps = model(images)\n",
    "        loss = criterion(log_ps, labels)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        acc1,  acc5 = get_accuracy(log_ps, labels, topk=(1, 5))\n",
    "        acc1_avg += acc1.item()\n",
    "        acc5_avg += acc5.item()\n",
    "        loss_avg += loss.item()\n",
    "        \n",
    "        if i%500 == 499:\n",
    "            print(\"Train Epoch: {epoch} Loss_avg：{loss:6.4f} \\t Acc@1_avg {acc1:6.2f} \\t Acc@5_avg {acc5:6.2f}\".format(epoch=epoch,\n",
    "                                                                  loss=loss_avg/(i+1),\n",
    "                                                                  acc1=acc1_avg/(i+1),\n",
    "                                                                  acc5=acc5_avg/(i+1) ))\n",
    "    \n",
    "    return loss_avg/len(data_loader),acc1_avg/len(data_loader),acc5_avg/len(data_loader)\n",
    "        \n",
    "    \n",
    "def val_model(epoch, data_loader, model, cirterion, optimizer, gpu_flag=False):\n",
    "    '''\n",
    "    验证函数\n",
    "    '''\n",
    "    if gpu_flag:\n",
    "        model.cuda().eval()\n",
    "    else:\n",
    "        model.cpu().eval()\n",
    "    \n",
    "    loss_avg = 0.0\n",
    "    acc1_avg=0.0\n",
    "    acc5_avg=0.0\n",
    "    for i, data in enumerate(data_loader):\n",
    "        images, labels = data\n",
    "        if gpu_flag:\n",
    "            images = Variable(images).cuda()\n",
    "            labels = Variable(labels).cuda()\n",
    "        else:\n",
    "            images = Variable(images)\n",
    "            labels = Variable(labels)\n",
    "        log_ps = model(images)\n",
    "        loss = criterion(log_ps, labels)\n",
    "        acc1,  acc5 = get_accuracy(log_ps, labels, topk=(1, 5))\n",
    "        acc1_avg += acc1.item()\n",
    "        acc5_avg += acc5.item()\n",
    "        loss_avg += loss.item()\n",
    "        \n",
    "        if i%500 == 499:\n",
    "            print(\"Val   Epoch: {epoch} Loss_avg：{loss:6.4f} \\t Acc@1_avg {acc1:6.2f} \\t Acc@5_avg {acc5:6.2f}\".format(epoch=epoch,\n",
    "                                                                  loss=loss_avg/(i+1),\n",
    "                                                                  acc1=acc1_avg/(i+1),\n",
    "                                                                  acc5=acc5_avg/(i+1) ))\n",
    "    \n",
    "    return loss_avg/len(data_loader),acc1_avg/len(data_loader),acc5_avg/len(data_loader)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 开始训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=> loading checkpoint 'checkpoints//2_resnet50_97.74.pth'\n",
      "=> loaded checkpoint 'checkpoints//2_resnet50_97.74.pth' (epoch 2)\n",
      "Start training!\n",
      "Epoch: 2 Loss_avg：0.0453 \t Acc@1_avg  98.17 \t Acc@5_avg  99.96 \t Time@   3.56\t TotalTime@  3.56\n",
      "epoch  3 set lr is  0.33\n",
      "0.0003\n",
      "Epoch: 3 Loss_avg：0.0247 \t Acc@1_avg  98.36 \t Acc@5_avg  99.96 \t Time@   3.84\t TotalTime@  7.41\n",
      "Epoch: 4 Loss_avg：0.0128 \t Acc@1_avg  98.42 \t Acc@5_avg  99.94 \t Time@   5.22\t TotalTime@ 12.63\n",
      "Finished training!\n"
     ]
    }
   ],
   "source": [
    "# 训练参数定义\n",
    "args.epochs = 5\n",
    "args.resume=\"checkpoints//2_resnet50_97.74.pth\"\n",
    "# args.resume=None\n",
    "\n",
    "# 训练主函数\n",
    "train_losses, val_losses, val_accuracyes = main()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "%xdel save_checkpoint"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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Cjkl9OJzhnW9WqWP2niAXUfPejAaEREREc9SQi8+p2WNsrQLDwMg2cIErXgjIiIihZgSevFJQ1P10s/ZdIBYVelFRESkkFJCLz6pefUytK97E+Co0o9foF56ERERKZyU0IvPGtYl3PXn7zbHs/NIggejEREREfEMJfTis26tVobIeo4qvbUwboF66UVERKTwUUIvPi11L/3cX+PZcVhVehERESlclNCLT2tatTSd6pcDUqr06qUXERGRwkUJvfi8oal66X/430G2H1KVXkRERAoPJfTi85pUKU2XBqmr9Ds8HJGIiIhI/lFCLwVC6l76H/53iN/jz3gwGhEREZH8o4ReCoRGlUPoenN5176q9CIiIlJYKKGXAiN1L/3Pvx3mt/jTHoxGREREJH8ooZcCo2GlEO5sWMG1P3a+VrwRERGRgk8JvRQoQ1JV6X/5/TBbDqhKLyIiIgWbEnopUBpULMXdjVNX6dVLLyIiIgWbEnopcIZ0rosxjj/P33qEX/ef8mxAIiIiInlICb0UOPUqBHN344quffXSi4iISEGmhF4KpKGdw11V+oXbjrBpn6r0IiIiUjApoZcCKbx8MN2bVHLtq5deRERECiol9FJgDelcx1WlX7z9KBv2nvRsQCIiIiJ5QAm9FFh1ygXT45arVfoPflGVXkRERAoeJfRSoA3uHI6fs0ofHXOM9XtOeDYgERERETdTQi8FWu2bStKraWXX/ge/aMUbERERKViU0EuB93zncPydZfplO4+xNk5VehERESk4lNBLgVczrES6Kr166UVERKTgUEIvhcLgznVcVfoVscdZveu4hyMSERERcQ8l9FIoVA8twX23pqrSa116ERERKSCU0Euh8XyncAKcVfpVu06wMlZVehEREfF9Suil0KgWWpzezaq49j+YvwNrrQcjEhEREck9JfRSqAzqVMdVpV+zW1V6ERER8X1K6KVQqVq2OH1bqEovIiIiBYcSeil0nutYh0B/R5V+bdxJlu085uGIRERERG6cEnopdKqUKU7fFlVd+x/8oiq9iIiI+C4l9FIoPdexDkH+jh//DXtPsTRGVXoRERHxTW5L6I0xVYwxnxtj4o0xl4wxccaYscaYMtm8PtQY84Qx5j/GmJ3GmAvGmNPGmGXGmMeNMVnGaoxpY4z5wRhzwhhz3hjzqzFmqDHG313vTwqWyqWL8UBL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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "image/png": {
       "height": 248,
       "width": 378
      },
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# %load AI/code/utils/loss_show.py\n",
    "%matplotlib inline\n",
    "%config InlineBackend.figure_format = 'retina'\n",
    "\n",
    "plt.plot(train_losses, label='Training loss')\n",
    "plt.plot(val_losses, label='Validation loss')\n",
    "# plt.plot(val_accuracyes, label='val_accuracy')\n",
    "plt.legend(frameon=False)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型单项预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "classes is 4 confidence is 0.9997\n"
     ]
    },
    {
     "data": {
      "image/png": "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O2w3UrubbH31SO3u+NUnDW5z4//3lZGOBays1trBi73f91AdtMLlALAF0BMFAAAwgBAFAAAwgBAFAAAwgBAFAAAwgIklAJhKVXVkkiP7L/fql3euquP7P1/QWnveihcGwMwTogCYVrdL8tgF7+3Tv5LkB0mEKABWnOF8AEyl1trRrbXayGvvSdcIwGwSogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAbwnCim0obvnjVSu0fe5NCR2t043x6pHQAAs0dPFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFAAAwABCFABTrapuXFXvqKofV9UVVbWuqo6tqutNujYAZtOaSRcAAEupqn2TnJxkzyT/nOT0JIckeWaS+1XVYa21CydYIgAzSE8UANPsb9MFqGe01o5srb2gtXbPJG9Icsskr5podQDMJCEKgKlUVfskuW+SdUn+74LVL09yWZLHVNWOK1waADNOiAJgWt2zX57QWrt6/orW2iVJvpTkOknutNKFATDb3BMFwLS6Zb88c4n1303XU3WLJP++1E6qau0Sq/YfvTQAZpmeKACm1S798qIl1s+9v+sK1AIAv6YnCoDVqvpl29hGrbWDF23c9VAdNO6iANjy6YkCYFrN9TTtssT6nRdsBwArQogCYFqd0S9vscT6/frlUvdMAcCyEKIAmFYn9cv7VtVv/HtVVddNcliS9Um+stKFATDbhCgAplJr7X+SnJBk7yRPW7D6FUl2TPLu1tplK1waADPOxBIATLOnJjk5yRur6l5JTktyxySHpxvG9+IJ1gbAjNITBcDU6nujfj/J8enC03OT7JvkjUnu3Fq7cHLVATCr9EQBMNVaaz9K8vhJ1wEAc/REAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADLBm0gUAwITsfdppp+Xggw+edB0ATMBpp52WJHuP0laIAmBW7bR+/foNp5566n9PupAps3+/PH2iVUwf12Vprs3iXJfFTdN12TvJxaM0FKIAmFXfSpLWmq6oeapqbeK6LOS6LM21WZzrsrgt5bq4JwoAAGCAkXuiPnP1B2uchQAAAKwGeqIAAAAGEKIAAAAGEKIAAAAGqNbapGsAAABYNfREAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAQAADCBEAbBFqKobV9U7qurHVXVFVa2rqmOr6nqT2M80ubbnVFW7V9UTq+qjVfW9qlpfVRdV1Rer6glVtSo/TyzH97qqHlNVrX89cZz1rpRxXpequmtVfbiqftLv6ydVdUJV3X85al9OY/wdc0R/Dc7u/y6dVVUfrKo7L1fty6WqHl5Vb6qqL1TVxf3P/XtH3Neq+t3rYbsArHpVtW+Sk5PsmeSfk5ye5JAkhyc5I8lhrbULV2o/02Qc51RVf57k75L8JMlJSX6Y5AZJHppklyQfTnJUW0UfKpbje11VN0nyzSRbJ9kpyZNaa28bZ93LbZzXpapekuSvklyQ5OPpfn72SHL7JCe11v5i7CewTMb4O+bVSf4iyYVJPpbu2vxukgclWZPkT1prI4WQSaiqrye5bZJLk5ydZP8k72utPXrgflbf797WmpeXl5eX16p+Jfl0kpbk6Qvef33//ltWcj/T9BrHOSW5Z5IHJtlqwft7pQtULcnDJn2uk/iZmdeukpyY5H+SvKbfxxMnfZ6Tui5Jjuq3/0yS6y6yfptJn+tKX5f+78uGJOcm2XPBusP7/Zw16XMdeF0OT7Jf//N/j/4c3jupn7uVfOmJAmBVq6p90n1wXZdk39ba1fPWXTfd/35Xug8tly33fqbJSpxTVb0oyauSvLm19vRrXfQKWI7rUlXPTPKGdB8k75nk5VllPVFj/Lu0VZLvpeut3Lu1dv5y1r3cxnhd7pjkK0n+pbX24EXWX5xulNh1x3sGK6Oq7pGup3pQT9Rq/d27KscwA8A89+yXJ8z/xzdJWmuXJPlSkuskudMK7WearMQ5XdUvf3Ut9rHSxnpdquqAJMckOa619vlxFrrCxnVdDk1y8ySfTPLz/h6g51fVM1fjfT8Z33X5bpIrkxxSVXvMX1FVd0ty3XS9mbNmVf7uFaIAWO1u2S/PXGL9d/vlLVZoP9NkWc+pqtYk+ZP+y0+Nso8JGdt16a/Be9INa3zRtS9tosZ1Xe7QL3+a5NR090Mdk+TYJCdX1X9U1fWvTaErbCzXpbX2syTPT9dD952qemtV/Z+q+kCSE9INfXzyGOpdbVbl7941ky4AAK6lXfrlRUusn3t/1xXazzRZ7nM6JsmBST7ZWvv0iPuYhHFel5elmyjhLq219de2sAkb13XZs1/+eZLvJ7l3kq8muVmS1yX5gyQfTDf0cTUY289La+3YqlqX5B1JnjRv1feSHN9aO2/UIlexVfm7V08UAFu66pfX9ibgce1nmox8TlX1jCTPTTeL1mPGWdQU2KzrUlWHpOt9el1r7cvLXtXkbe7Py9bztn94a+3fW2uXtta+neQh6WZxu/sqHdq3mM3+e1RVf5HkQ0mOT7Jvkh2THJzkrCTvq6q/WaYaV7Op/N0rRAGw2s39L+UuS6zfecF2y72fabIs51RVT0tyXJLvJDm8H6a0mlzr6zJvGN+ZSV46vtImalw/Lz/vl2e11v57/oq+t26u1/KQwRVOxliuSz/xwqvTTSzxnNbaWa21y1trp6YLl+ckeW4/0cIsWZW/e4UoAFa7M/rlUuPl9+uXS423H/d+psnYz6mqnpXkzUm+lS5AnTt6eRMzjuuyU9/+gCS/nPeA3ZZuZr4k+Yf+vWOvdcUrY9x/l36xxPq5kLXDZtY1aeO6Lg/olyctXNFauzzJKek+m99+aIGr3Kr83eueKABWu7kPJPetqq0WmR73sCTr000tvBL7mSZjPaeqen66+6C+nuQ+rbULxlzvShnHdbkiyduXWHdQug/CX0z3AXG1DPUb18/L59PN1rhfVW3bWrtywfoD++W6a1/yihjXddmuXy41qcbc+wuv15ZuVf7u1RMFwKrWWvufdDNb7Z3kaQtWvyLdPQfvnnu+SFVtU1X7V9W+12Y/q8G4rk2/7qXpAtTaJPdaxQFqLNeltba+tfbExV5J/qXf7F39e+9f9pMagzH+XbogyfvTDc962fx1VXWfdBNLXJRVMqPjGP8efaFf/llV3Wj+iqr6w3Rh4ZdJTh7vGUyHLe13r4ftArDq9f8on5xuVrB/TnJakjsmOTzdEJBDW2sX9tvunW7GsB+01vYedT+rxTiuTVU9Nt2N8BuSvCmL35uwrrV2/PKcxfiN62dmiX0fnVX4sN1krH+X9kz3fJ/fTRceTkk3O99D0k0Q8KjW2geX/YTGZEx/j7ZKdz/YvZNckuSjSc5NNyT0AekmUHhWa+24lTincaiqI5Mc2X+5V7qAfFauCYwXtNae12+7d7ak372tNS8vLy8vr1X/SnKTJO9M93T7K5P8IN3kB7st2G7vdB/i1l2b/aym17W9NkmO7t/f2Otzkz7PSf3MLLLfuev1xEmf4ySvS5Ldkrw+3QfnK5NcmO4D8p0mfY6Tui5JtknyrHRD0y5ON+zxvHTP0rrvpM9xhGuyqd8N6+Ztu0X97tUTBQAAMIB7ogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAYQogAAAAb4/wEoAzk80rc9ngAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<Figure size 432x648 with 2 Axes>"
      ]
     },
     "metadata": {
      "image/png": {
       "height": 235,
       "width": 424
      },
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# %%writefile AI/code/detect/class_detect_one.py\n",
    "def view_classify(img, ps):\n",
    "    ''' Function for viewing an image and it's predicted classes.\n",
    "    '''\n",
    "    ps = ps.cpu().data.numpy().squeeze()\n",
    "    img = img.cpu()\n",
    "    \n",
    "    fig, (ax1, ax2) = plt.subplots(figsize=(6,9), ncols=2)\n",
    "    ax1.imshow(img.resize_(1, 28, 28).numpy().squeeze())\n",
    "    ax1.axis('off')\n",
    "    ax2.barh(np.arange(10), ps)\n",
    "    ax2.set_aspect(0.1)\n",
    "    ax2.set_yticks(np.arange(10))\n",
    "    ax2.set_yticklabels(np.arange(10))\n",
    "    ax2.set_title('Class Probability')\n",
    "    ax2.set_xlim(0, 1.1)\n",
    "\n",
    "    plt.tight_layout()\n",
    "    \n",
    "%matplotlib inline\n",
    "def test_prediction(model, data):\n",
    "    images, labels = next(iter(data))\n",
    "    img = images[42].view(1, 784)\n",
    "    \n",
    "    with torch.no_grad():\n",
    "        if args.gpu > -1:\n",
    "            model = model.cuda()\n",
    "            img = img.cuda()\n",
    "            logps = model(img)\n",
    "        else:\n",
    "            model = model.cpu()\n",
    "            img = img.cpu()\n",
    "            logps = model(img)\n",
    "\n",
    "    # 网络的输出是对数概率，需要对概率取指数\n",
    "    ps = torch.exp(logps)\n",
    "    confid, classes = ps.topk(1)\n",
    "    print(\"classes is {classes} confidence is {confid:6.4f}\".format(classes=classes.item(), confid=confid.item()))\n",
    "    view_classify(img.view(1, 28, 28), ps)\n",
    "test_prediction(model, test_loader)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Tensor' object has no attribute 'to_device'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-34-9f1df0aee82d>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mtorch\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mrand\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m2\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mto_device\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m'cuda:0'\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mAttributeError\u001b[0m: 'Tensor' object has no attribute 'to_device'"
     ]
    }
   ],
   "source": [
    "torch.rand(2).to_device('cuda:0')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 模型批量预测"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting AI/code/detect/class_detect_any.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile AI/code/detect/class_detect_any.py\n",
    "def test_model(test_loader, model):\n",
    "    model.eval()\n",
    "    \n",
    "    submission = [['ImageId', 'Label']]\n",
    "    image_id = 1\n",
    "    for images, _ in test_loader:\n",
    "        if args.gpu > -1:\n",
    "            model = model.cuda()\n",
    "            images = images.cuda()\n",
    "            log_ps = model(images)\n",
    "        else:\n",
    "            model = model.cpu()\n",
    "            images = images.cpu()\n",
    "            loglog_psps = model(images)\n",
    "            \n",
    "        ps = torch.exp(log_ps)\n",
    "        top_p, top_class = ps.topk(1, dim=1)\n",
    "        \n",
    "        for prediction in top_class:\n",
    "            submission.append([image_id, prediction.item()])\n",
    "            image_id += 1\n",
    "    return submission\n",
    "submission = test_model(test_loader, model)\n",
    "\n",
    "submission_df = pd.DataFrame(submission)\n",
    "submission_df.columns = submission_df.iloc[0]\n",
    "submission_df = submission_df.drop(0, axis=0)\n",
    "\n",
    "submission_df.to_csv(\"submission.csv\", index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from AI.models.efficientnet import EfficientNet\n",
    "import torch\n",
    "\n",
    "model = EfficientNet.from_name('efficientnet-b0')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 1000])"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model(torch.rand(1,3,224,224)).shape"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.6"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "203px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
